package com.micro.frog.job.config;

import com.alibaba.fastjson.JSON;
import com.micro.frog.core.common.Result;
import com.micro.frog.core.contants.MaGlobal;
import com.micro.frog.job.model.entity.Job;
import com.micro.frog.job.model.entity.JobLog;
import com.micro.frog.job.service.JobLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 任务日志 【切面】 统一处理配置
 * 知识点： Aop
 * 执行顺序： Aop--> @Around(运行 pjp.proceed()之前的代码段)--> @Before --> Method(目标方法) -->
 * --> @Around(运行 pjp.proceed()之后面的代码段) --> @After --> @AfterReturning/@AfterThrowing
 *
 * @author :  ljh
 * @date : 2018/5/11
 **/
@Slf4j
@Aspect
@Configuration
public class LogAspectConfig {

    @Autowired
    JobLogService jobLogService;

    /**
     * 【切入点】 方法体 不会被执行
     * 所有标记有 ScheduleTask 的方法都会进入此切入点
     */
    @Pointcut(value = "@annotation(com.micro.frog.core.annotations.ScheduleTask)")
    public void recordLog() {
        log.info("进入切点,这里不会执行");
    }

    /**
     * 环绕通知
     * 在切点标记方法运行之前 处理
     * 用这个注解的方法入参传的是ProceedingJionPoint pjp，
     * 可以决定当前线程能否进入核心方法中 —— 通过调用 pjp.proceed()运行目标方法;
     *
     * @return 注意：这里必须有返回值，如果不返回 @AfterReturning 就无法接收到 返回值
     */
    @Around(value = "recordLog()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        log.info("@Around 正在校验环境，准备执行目标...");
        Result result = new Result();
        long startTime = System.currentTimeMillis();
        Map data = new HashMap();
        try {
            result = (Result) pjp.proceed();
            data.put("creator", result.getData());
            log.info("@Around proceed 执行完毕 目标执行完毕，准备后续处理");
        } catch (Throwable ex) {
            result.setFlag(false);
            result.setMsg(ex.getMessage());
        }
        long endTime = System.currentTimeMillis();
        data.put("spendTime", new Integer((endTime - startTime) + ""));
        result.setData(data);
        return result;
    }

    /**
     * 前置通知
     * 方法运行前执行
     */
    @Before(value = "recordLog()")
    public void doBefore(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        Job job = new Job();
        BeanUtils.copyProperties(((Object[]) args[0])[0], job);
        log.info("@Before 校验环境完毕，开始执行【{}】,参数：{}", job.getName(), job.getParams());
    }

    /**
     * 后置通知
     */
    @After(value = "recordLog()")
    public void doAfter(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        Job job = new Job();
        BeanUtils.copyProperties(((Object[]) args[0])[0], job);
        log.info("@After 【{}】任务执行后续处理(目标处理结果此时还没有返回)...", job.getName());
    }

    @AfterReturning(returning = "result", pointcut = "recordLog()")
    public void doAfterReturning(JoinPoint joinPoint, Result result) {

        Object[] args = joinPoint.getArgs();
        Job job = new Job();
        BeanUtils.copyProperties(((Object[]) args[0])[0], job);
        log.info("@AfterReturning 任务执行完成 结果:{}", result);

        Map data = (Map) result.getData();

        final Date now = new Date();
        JobLog jobLog = new JobLog();
        jobLog.setTaskId(job.getId());
        jobLog.setCreator(null == data.get("creator") ? MaGlobal.SYSTEM : (String) data.get("creator"));
        jobLog.setCreatedTime(now);
        jobLog.setSpendTime((Integer) data.get("spendTime"));
        result.setData(null);
        jobLog.setResult(JSON.toJSONString(result));
        jobLog.setStatus(result.isFlag() ? "1" : "-1");
        jobLogService.add(jobLog);
    }

    @AfterThrowing(throwing = "ex", pointcut = "recordLog()")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable ex) {

        log.info("@AfterThrowing 任务执行抛出异常 异常信息:{}", ex.getMessage());

        Object[] args = joinPoint.getArgs();
        Job job = new Job();
        BeanUtils.copyProperties(((Object[]) args[0])[0], job);

        final Date now = new Date();
        JobLog jobLog = new JobLog();
        jobLog.setTaskId(job.getId());
        jobLog.setStatus("0");
        jobLog.setSpendTime(0);
        jobLog.setCreator(MaGlobal.SYSTEM);
        jobLog.setCreatedTime(now);
        jobLog.setResult(ex.getMessage());
        jobLogService.add(jobLog);
    }
}
